Data
acc1_cancer_cells = readRDS("./Data/acc1_cancer_cells_15KnCount_V3.RDS")
all_acc_cancer_cells = readRDS("./Data/acc_cancer_cells_V3.RDS")
acc_all_cells = readRDS("./Data/acc_tpm_nCount_mito_no146_15k_with_ACC1_.RDS")
luminal_pathways = c("CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_UP","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_UP","HUPER_BREAST_BASAL_VS_LUMINAL_DN","LIM_MAMMARY_LUMINAL_PROGENITOR_UP","SMID_BREAST_CANCER_LUMINAL_B_UP" )
# add luminal pathways
luminal_gs = msigdbr(species = "Homo sapiens") %>%as.data.frame() %>% dplyr::filter(gs_name %in% luminal_pathways)%>% dplyr::distinct(gs_name, gene_symbol) %>% as.data.frame()
Parameters
suffix = "01_25"
data_to_read = ""
functions
source_from_github(repositoy = "DEG_functions",version = "0.2.24")
ℹ SHA-1 hash of file is a183df1c565702ecd8ed338bb2abfb0e13415d8e
source_from_github(repositoy = "HMSC_functions",version = "0.1.12",script_name = "functions.R")
ℹ SHA-1 hash of file is 2934dee5f6b9fee69635192f19b1bcc205e05b62
source_from_github(repositoy = "cNMF_functions",version = "0.3.53",script_name = "cnmf_function_Harmony.R")
ℹ SHA-1 hash of file is a6f56985fe95e4026c7830d7100dd81dd950df3b
UMAP
DimPlot(object = acc1_cancer_cells,pt.size = 2)

features UMAP
FeaturePlot(object = acc1_cancer_cells,features = c("TP63","ACTA2","IL12B","CNN1"))
Warning in FeaturePlot(object = acc1_cancer_cells, features = c("TP63", :
All cells have the same value (0) of IL12B.

Enrichment analysis HMSC vs ACC
patient.ident = all_acc_cancer_cells$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells = AddMetaData(object = all_acc_cancer_cells,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells = SetIdent(all_acc_cancer_cells, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

cell cycle filtering
hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets =getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=all_acc_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(all_acc_cancer_cells@assays$RNA@scale.data[intersect(geneIds,var_features),],2,mean)
all_acc_cancer_cells=AddMetaData(all_acc_cancer_cells,score,hallmark_name)
#filter:
all_acc_cancer_cells_ccFiltered=all_acc_cancer_cells[,all_acc_cancer_cells@meta.data[[hallmark_name]]< 0.3]
min_threshold = min(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
max_threshold = max(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
Before cc filtering
FeaturePlot(object = all_acc_cancer_cells,features = hallmark_name) + ggtitle("Before cc filtering") & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

After cc filtering
FeaturePlot(object = all_acc_cancer_cells_ccFiltered,features = hallmark_name) + ggtitle("After cc filtering") & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Enrichment analysis filtered HMSC vs ACC
patient.ident = all_acc_cancer_cells_ccFiltered$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells_ccFiltered = AddMetaData(object = all_acc_cancer_cells_ccFiltered,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells_ccFiltered = SetIdent(all_acc_cancer_cells_ccFiltered, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells_ccFiltered, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells_ccFiltered))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells_ccFiltered),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

MYB expression
all_acc_cancer_cells = SetIdent(object = all_acc_cancer_cells,value = "patient.ident") #active snn graph
FeaturePlot(object = all_acc_cancer_cells,features = "MYB",label = T)

CNV
new.cluster.ids <- c("cancer", #0
"cancer", #1
"CAF", #2
"cancer", #3
"Endothelial", #4
"cancer", #5
"cancer", #6
"CAF", #7
"CAF", #8
"CAF", #9
"cancer", #10
"CAF", #11
"cancer", #12
"cancer", #13
"cancer", #14
"cancer", #15
"cancer", #16
"WBC", #17
"CAF" #18
)
#rename idents:
acc_all_cells = SetIdent(object = acc_all_cells,value = "RNA_snn_res.1") #active snn graph
names(new.cluster.ids) <- levels(acc_all_cells) #add snn graph levels to new.cluster.ids
acc_all_cells@meta.data[["seurat_clusters"]] = acc_all_cells@meta.data[["RNA_snn_res.1"]]
acc_all_cells = SetIdent(object = acc_all_cells,value = "seurat_clusters")
acc_all_cells <- RenameIdents(acc_all_cells, new.cluster.ids)
# divide "cancer" into patients:
cell_types = acc_all_cells@active.ident %>% as.data.frame()
cell_types[,1]<- as.character(cell_types[,1])
cell_types = cbind(cell_types,acc_all_cells$patient.ident) %>% setnames(old = names(.),
new = c('cell_type','patient'))
cell_types[cell_types$cell_type == "cancer",] = cell_types[cell_types$cell_type == "cancer",2]
# hmsc_rows = (startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer"
# acc_rows = !(startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer"
# cell_types[,1][hmsc_rows] = "HMSC"
# cell_types[,1][acc_rows] = "ACC"
#add to metadata:
cell_types[,2] = NULL
cell_types[cell_types$cell_type == "ACC1",] = "HMSC"
acc_all_cells = AddMetaData(object =acc_all_cells ,metadata = cell_types,col.name = "cell.type")
CNV UMAP

CNV plot
## {-}
CNV subtypes
cnv_subtypes = as.data.frame(cutree(infercnv_obj_default@tumor_subclusters[["hc"]][["HMSC"]], k = 2))
names(cnv_subtypes)[1] = "cnv.cluster"
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "-",replacement = "\\.")
infercnv.observations = data.frame(fread(file = "./Data/inferCNV/infercnv.observations.txt"), row.names=1)
Warning in fread(file = "./Data/inferCNV/infercnv.observations.txt") :
Detected 1332 column names but the data has 1333 columns (i.e. invalid file). Added 1 extra default column name for the first column which is guessed to be row names or an index. Use setnames() afterwards if this guess is not correct, or fix the file write command that created the file to create a valid file.
names_to_keep = colnames(infercnv.observations) %in% (colnames(acc1_cancer_cells) %>% gsub(pattern = "_",replacement = "\\."))
infercnv.observations = infercnv.observations[,names_to_keep]

rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "2\\.",replacement = "2_")
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "3\\.",replacement = "3_")
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = cnv_subtypes)
DimPlot(acc1_cancer_cells,group.by = "cnv.cluster",pt.size = 2,cols =colors)

Original score
original_myo_genes = c( "TP63", "TP73", "CAV1", "CDH3", "KRT5", "KRT14", "ACTA2", "TAGLN", "MYLK", "DKK3")
original_lum_genes = c("KIT", "EHF", "ELF5", "KRT7", "CLDN3", "CLDN4", "CD24", "LGALS3", "LCN2", "SLPI" )
calculate_score(dataset = all_acc_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes)
correlation of lum score and myo score: -0.45
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1



Original score of ACC1
calculate_score(dataset = acc1_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes,lum_threshold = 0,myo_threshold = 0)
correlation of lum score and myo score: 0.06
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1



0.35 Most correlated score
Myo genes
myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.35)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes = 20"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
[1] "COL16A1" "RP1-39G22.4" "ACTG2" "CD200" "MYLK" "TP63" "KCNMB1"
[8] "ADAMTS2" "LOXL2" "TPM2" "CLIC3" "SNCG" "ACTA2" "TAGLN"
[15] "A2M" "NGFR" "CNN1" "PPP1R14A" "MYL9" "POM121L9P"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes)
[1] "MYLK" "TP63" "ACTA2" "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = rownames(acc1_cancer_cells),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res
Lum genes
lum_protein_markers = c("KIT")
top_lum = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.35,n_vargenes = 5000)
Calculating gene variances
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes = 5"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
[1] "B3GNT2" "GLRB" "EFNA5" "ALDH3B2" "CCND1"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes)
character(0)
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = rownames(acc1_cancer_cells),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res
enriched genes score
rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = -2.5,myo_threshold = -2.5)
correlation of lum score and myo score: -0.16
correlation of lum score and original lum score: 0.43
correlation of myo score and original myo score: 0.79



0.2 Most correlated score
myo Genes
n_vargenes = 2000
myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.2,n_vargenes = n_vargenes)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes = 48"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
[1] "PLOD1" "RP1-39G22.4" "PDZK1" "CSRP1" "CHI3L1" "HIST3H3"
[7] "AC104699.1" "ACTG2" "LYG1" "DALRD3" "CD200" "RP11-627C21.1"
[13] "FGFBP2" "IGFBP7" "HSPB3" "RP11-168A11.4" "SPARC" "CD83"
[19] "HLA-DOB" "TBCC" "NFKBIE" "MIR3662" "SMOC2" "FBXL6"
[25] "TPM2" "SNCG" "ACTA2" "DKK3" "MIR7113" "CTA-797E19.3"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes)
[1] "ACTA2" "DKK3" "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = VariableFeatures(acc1_cancer_cells) %>% head(n_vargenes),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res
Lum Genes
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = 15000)
Calculating gene variances
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
lum_protein_markers = c("KIT")
n_vargenes = 2000
top_lum = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.3,n_vargenes = n_vargenes)
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes = 13"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
[1] "CSDE1" "RRP9" "TKT" "TFG" "ATP1B3" "EFNA5" "GABRP" "CALML5" "MMP7" "SNORD68"
[11] "APP" "SDF2L1" "SAT1"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes)
character(0)
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = VariableFeatures(acc1_cancer_cells) %>% head(n_vargenes),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res
calculate_score(dataset = acc1_cancer_cells,myo_genes = top_myo,lum_genes = top_lum,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: -0.02
correlation of lum score and original lum score: 0.65
correlation of myo score and original myo score: 0.77



myo_intersected = intersect(top_myo,original_myo_genes)
lum_intersected = intersect(top_lum,original_lum_genes)
message("genes in myo score:")
genes in myo score:
myo_intersected
[1] "ACTA2" "DKK3" "TAGLN"
message("genes in lum score:")
genes in lum score:
lum_intersected
[1] "LGALS3"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_intersected,lum_genes = lum_intersected,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: 0.03
correlation of lum score and original lum score: 0.69
correlation of myo score and original myo score: 0.81



enriched genes
rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
message("genes in myo score:")
genes in myo score:
myo_enriched_genes
[1] "FBLIM1" "NEXN" "NMNAT2" "MYLK" "CCDC50" "IGFBP7" "RAI14" "ARAP3" "SPARC" "CALD1" "LOXL2"
[12] "COL5A1" "ACTA2" "DKK3" "MSRB3" "COL4A1" "ACTN1" "TPM1" "TGFB1I1" "ADORA2B" "MXRA7" "CNN1"
[23] "TP63" "ACTA2"
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
[1] "PADI2" "PATJ" "PEX11B" "APH1A" "C1orf43" "EFNA4" "NECTIN4" "SCYL3" "ELF3"
[10] "SOX13" "IRF6" "MED28" "EPB41L4A" "RGL2" "C6orf132" "TPD52L1" "ICA1" "MACC1"
[19] "TRPS1" "FAM83H" "RASEF" "ARRDC1" "COMMD3" "ANK3" "GSTO2" "PDCD4" "EHF"
[28] "ALDH3B2" "SHANK2" "SORL1" "FKBP4" "PTPN6" "DUSP16" "RERG" "ADCY6" "ERBB3"
[37] "ERP29" "SUSD6" "RPS6KA5" "SPINT1" "FEM1B" "TLE3" "SCAMP2" "CLN3" "ADGRG1"
[46] "ATP2C2" "GGT6" "MYO1D" "ST6GALNAC2" "CYB5A" "BLVRB" "VRK3" "SYCP2" "TMPRSS2"
[55] "LIMK2" "KIT"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 0,myo_threshold = -1)
correlation of lum score and myo score: 0.1
correlation of lum score and original lum score: 0.71
correlation of myo score and original myo score: 0.76



enriched genes and in original score
myo_enriched_genes = myo_enriched_genes[myo_enriched_genes %in% original_myo_genes]
lum_enriched_genes = lum_enriched_genes[lum_enriched_genes %in% original_lum_genes]
message("genes in myo score:")
genes in myo score:
myo_enriched_genes
[1] "MYLK" "ACTA2" "DKK3" "TP63" "ACTA2"
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
[1] "EHF" "KIT"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 2,myo_threshold = 2)
correlation of lum score and myo score: -0.07
correlation of lum score and original lum score: 0.62
correlation of myo score and original myo score: 0.77



HPV
Only HMSC cancer cells:
HPV33_P3 = fread("./Data/HPV33_P3.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P3.df = HPV33_P3 %>% mutate(
plate = gsub(x =HPV33_P3$plate, replacement = "",pattern = "_.*$")
%>% gsub(pattern = "-P",replacement = ".P")
%>% gsub(pattern = "-",replacement = "_",)
)
HPV33_P3.df = HPV33_P3.df %>% dplyr::filter(HPV33_P3.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P3.df) <- HPV33_P3.df$plate
HPV33_P3.df$plate = NULL
HPV33_P2 = fread("./Data/HPV33_P2.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P2.df = HPV33_P2 %>% mutate(
plate = gsub(x =HPV33_P2$plate, replacement = "",pattern = "_.*$")
%>% gsub(pattern = "plate2-",replacement = "plate2_",)
%>% gsub(pattern = "-",replacement = "\\.",)
)
HPV33_P2.df = HPV33_P2.df %>% dplyr::filter(HPV33_P2.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P2.df) <- HPV33_P2.df$plate
HPV33_P2.df$plate = NULL
HPV33 = rbind(HPV33_P3.df,HPV33_P2.df)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = HPV33,col.name = "HPV33.reads")
FeaturePlot(acc1_cancer_cells,features = "HPV33.reads",max.cutoff = 40)

data = FetchData(object = acc1_cancer_cells,vars = "HPV33.reads")
print(
data %>%
ggplot(aes( x=HPV33.reads)) +
geom_density()
)

hpv33_positive = HPV33 %>% dplyr::mutate(hpv33_positive = case_when(reads >= 750 ~"strong positive",
reads < 750 & reads > 10 ~ "positive",
reads < 10 ~ "negative")
)
hpv33_positive$reads = NULL
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = hpv33_positive)
DimPlot(object = acc1_cancer_cells,group.by = c("hpv33_positive"),pt.size = 2)

cNMF
library(reticulate)
#write expression
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = 2000)
vargenes = VariableFeatures(object = acc1_cancer_cells)
hmsc_expression = t(as.matrix(GetAssayData(acc1_cancer_cells,slot='data')))
hmsc_expression = 2**hmsc_expression #convert from log2(tpm+1) to tpm
hmsc_expression = hmsc_expression-1
# hmsc_expression = hmsc_expression[,!colSums(hmsc_expression==0, na.rm=TRUE)==nrow(hmsc_expression)] #delete rows that have all 0
hmsc_expression = hmsc_expression[,vargenes]
write.table(x = hmsc_expression ,file = './Data/cNMF/hmsc_expressionData_2Kvargenes.txt',sep = "\t")
from cnmf import cNMF
name = 'HMSC_cNMF_2Kvargenes'
outdir = './Data/cNMF'
K_range = np.arange(3,10)
cnmf_obj = cNMF(output_dir=outdir, name=name)
counts_fn='./Data/cNMF/hmsc_expressionData_2Kvargenes.txt'
tpm_fn = counts_fn ## This is a weird case where because this dataset is not 3' end umi sequencing, we opted to use the TPM matrix as the input matrix rather than the count matrix
cnmf_obj.prepare(counts_fn=counts_fn, components=K_range, seed=14,tpm_fn=tpm_fn)
cnmf_obj.factorize(worker_i=0, total_workers=1)
cnmf_obj.combine()
cnmf_obj.k_selection_plot()
Save object
import pickle
f = open('./Data/cNMF/HMSC_cNMF_2Kvargenes/cnmf_obj.pckl', 'wb')
pickle.dump(cnmf_obj, f)
f.close()
Load object
from cnmf import cNMF
import pickle
f = open('./Data/cNMF/HMSC_cNMF_2Kvargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
selected_k = 4
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/scanpy/preprocessing/_simple.py:843: UserWarning: Received a view of an AnnData. Making a copy.
view_to_actual(adata)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
# calculate usage by expression*genes coefs:
gep_scores = py$gep_scores
all_metagenes= py$usage_norm
Enrichment analysis by top 200 genes of each program
plt_list = list()
for (i in 1:ncol(gep_scores)) {
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),200) #take top top_genes_num
res = genes_vec_enrichment(genes = top,background = rownames(gep_scores),homer = T,title =
i,silent = T,return_all = T)
plt_list[[i]] = res$plt
}
gridExtra::grid.arrange(grobs = plt_list)

plt_list = list()
for (i in 1:ncol(gep_scores)) {
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),10) #take top top_genes_num
message(paste("program ",i,"top genes:"))
print(top)
}
program 1 top genes:
[1] "IGKV5-2" "NDUFB7" "LGALS1" "UBL5" "S100A6" "S100A11" "CUTA" "IFITM3" "JTB" "IL17RB"
program 2 top genes:
[1] "EGR1" "C6orf62" "JUNB" "DNAJA1" "ATF3" "IER2" "ERRFI1" "KLF6" "CDKN1A" "MTHFD2"
program 3 top genes:
[1] "RP1-128M12.3" "RP11-374F3.3" "RP11-403A21.3" "RP11-454L9.2" "AC097500.1" "RP11-463H24.4"
[7] "RP11-515O17.2" "RP11-536L3.4" "PALD1" "AL049758.2"
program 4 top genes:
[1] "LTF" "PIGR" "FMO2" "HSD17B2" "MLPH" "PRR15L"
[7] "RF00019.219" "RP11-817J15.2" "CD14" "CLU"
# Make metagene names
for (i in 1:ncol(all_metagenes)) {
colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% select(i)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = metage_metadata)
}
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(i)` instead of `i` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
FeaturePlot(object = acc1_cancer_cells,features = colnames(all_metagenes))

assignment UMAP
larger_by = 2
message(paste("larger_by = ", larger_by))
larger_by = 2
acc1_cancer_cells = program_assignment(dataset = acc1_cancer_cells,larger_by = larger_by,program_names = colnames(all_metagenes))
selected_k =4
colors = rainbow(selected_k)
colors = c(colors,"grey")
DimPlot(acc1_cancer_cells,group.by = "program.assignment",pt.size = 2,cols =colors)

show cell cycle program:
hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets =GSEABase::getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=acc1_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(acc1_cancer_cells@assays$RNA@data[intersect(geneIds,var_features),],2,mean)
acc1_cancer_cells=AddMetaData(acc1_cancer_cells,score,hallmark_name)
FeaturePlot(object = acc1_cancer_cells,features = hallmark_name)

cc_vs_program2 = FetchData(object = acc1_cancer_cells,vars = c("metagene.2",hallmark_name))
cor(cc_vs_program2[1],cc_vs_program2[2])
GO_MITOTIC_CELL_CYCLE
metagene.2 0.611673
Comparisions
cnv_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","hpv33_positive"))
test <- fisher.test(table(cnv_vs_hpv))
ggbarstats(
cnv_vs_hpv, cnv.cluster, hpv33_positive,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

cnv_vs_cnmf = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","cnv.cluster"))
cnv_vs_cnmf = cnv_vs_cnmf %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnv_vs_cnmf))
ggbarstats(
cnv_vs_cnmf, program.assignment, cnv.cluster,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

cnmf_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","hpv33_positive"))
cnmf_vs_hpv = cnmf_vs_hpv %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnmf_vs_hpv))
ggbarstats(
cnmf_vs_hpv, program.assignment, hpv33_positive,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

myb_vs_cnv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","MYB"))
myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )
ggboxplot(myb_vs_cnv, x = "cnv.cluster", y = "MYB",
palette = "jco",
add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("1","2")))

myb_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("hpv33_positive","MYB"))
myb_vs_hpv $hpv33_positive = as.character(myb_vs_hpv $hpv33_positive )
ggboxplot(myb_vs_hpv, x = "hpv33_positive", y = "MYB",
palette = "jco",
add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("positive","negative"),c("strong positive", "positive"),c("strong positive", "negative")))+ stat_summary(fun.data = function(x) data.frame(y=15, label = paste("Mean=",round(mean(x),digits = 2))), geom="text") +ylab("log2(MYB)")
Warning in wilcox.test.default(c(7.30445531374182, 0, 7.65159269158999, :
cannot compute exact p-value with ties

hpvReads_vs_myb = FetchData(object = acc1_cancer_cells,vars = c("HPV33.reads","MYB"))
corr = cor(hpvReads_vs_myb$HPV33.reads,hpvReads_vs_myb$MYB)
print("correlation of MYB abd hpv33_reads:" %>% paste(corr %>% round(digits = 2)) )
[1] "correlation of MYB abd hpv33_reads: 0.21"
acc1_cancer_cells_data = acc1_cancer_cells@assays[["RNA"]]@data %>% as.data.frame()
acc1_cancer_cells_data = cor(acc1_cancer_cells_data)
annotation = FetchData(object = acc1_cancer_cells,vars = c("orig.ident"))
colors <- c(seq(-1,1,by=0.01))
my_palette <- c("blue",colorRampPalette(colors = c("blue", "white", "red"))
(n = length(colors)-3), "red")
pht1 = pheatmap(acc1_cor,annotation_col = annotation,fontsize = 6,breaks = colors, color = my_palette,show_colnames = F,show_rownames = F)
acc1_cancer_cells_data = acc1_cancer_cells@assays[["RNA"]]@data %>% as.data.frame()
acc1_cancer_cells_data = cor(acc1_cancer_cells_data)
annotation = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster"))
colors <- c(seq(-1,1,by=0.01))
my_palette <- c("blue",colorRampPalette(colors = c("blue", "white", "red"))
(n = length(colors)-3), "red")
pht1 = pheatmap(acc1_cor,annotation_col = annotation,fontsize = 6,breaks = colors, color = my_palette,show_colnames = F,show_rownames = F)
cnv_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","orig.ident"))
test <- fisher.test(table(cnv_vs_plate))
ggbarstats(
cnv_vs_plate, cnv.cluster, orig.ident,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

plate3 = cnv_vs_plate %>% dplyr::filter( orig.ident == "ACC1.P3") %>% rownames() %>% gsub(pattern = "_",replacement = ".")
plate2 = cnv_vs_plate %>% dplyr::filter( (orig.ident == "ACC.plate2")) %>% rownames() %>% gsub(pattern = "_",replacement = ".")

cnmf_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","orig.ident"))
cnmf_vs_plate= cnmf_vs_plate %>% dplyr::filter(program.assignment == "1" | program.assignment == "2")
test <- fisher.test(table(cnmf_vs_plate))
ggbarstats(
cnmf_vs_plate, program.assignment, orig.ident,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

plate_3 = cnmf_vs_plate %>% dplyr::filter((program.assignment == 1 & orig.ident == "ACC1.P3") ) %>% rownames()
plate_2 = cnmf_vs_plate %>% dplyr::filter((program.assignment == 2 & orig.ident == "ACC.plate2")) %>% rownames()
cells = list(plate_3 = plate_3,plate_2 = plate_2)
Results
exceptions
DimPlot(object = acc1_cancer_cells, cells.highlight = cells, cols.highlight = c("green","red"), cols = "gray", order = TRUE,pt.size = 2,sizes.highlight = 2) + ggtitle("exceptions")
program.assignment
DimPlot(acc1_cancer_cells,group.by = "program.assignment",pt.size = 2,cols =colors)
LS0tCnRpdGxlOiAiVGl0bGUiCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KYGBge3IsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBpbmNsdWRlID0gVAopCmBgYAoKIyMgRGF0YQoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gcmVhZFJEUygiLi9EYXRhL2FjYzFfY2FuY2VyX2NlbGxzXzE1S25Db3VudF9WMy5SRFMiKQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IHJlYWRSRFMoIi4vRGF0YS9hY2NfY2FuY2VyX2NlbGxzX1YzLlJEUyIpCmFjY19hbGxfY2VsbHMgPSByZWFkUkRTKCIuL0RhdGEvYWNjX3RwbV9uQ291bnRfbWl0b19ubzE0Nl8xNWtfd2l0aF9BQ0MxXy5SRFMiKQpsdW1pbmFsX3BhdGh3YXlzID0gYygiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfQkFTQUxfRE4iLCJDSEFSQUZFX0JSRUFTVF9DQU5DRVJfTFVNSU5BTF9WU19CQVNBTF9VUCIsIkNIQVJBRkVfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX1ZTX01FU0VOQ0hZTUFMX0ROIiwiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfTUVTRU5DSFlNQUxfVVAiLCJIVVBFUl9CUkVBU1RfQkFTQUxfVlNfTFVNSU5BTF9ETiIsIkxJTV9NQU1NQVJZX0xVTUlOQUxfUFJPR0VOSVRPUl9VUCIsIlNNSURfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX0JfVVAiICkKCiMgYWRkIGx1bWluYWwgcGF0aHdheXMKbHVtaW5hbF9ncyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKSAlPiVhcy5kYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSAlaW4lIGx1bWluYWxfcGF0aHdheXMpJT4lIGRwbHlyOjpkaXN0aW5jdChnc19uYW1lLCBnZW5lX3N5bWJvbCkgJT4lIGFzLmRhdGEuZnJhbWUoKQoKYGBgCgojIyBQYXJhbWV0ZXJzCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpzdWZmaXggPSAiMDFfMjUiCmRhdGFfdG9fcmVhZCA9ICIiCmBgYAoKCiMjIGZ1bmN0aW9ucwoKYGBge3Igd2FybmluZz1GQUxTRX0Kc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJERUdfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMi4yNCIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiSE1TQ19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4xLjEyIixzY3JpcHRfbmFtZSA9ICJmdW5jdGlvbnMuUiIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiY05NRl9mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4zLjUzIixzY3JpcHRfbmFtZSA9ICJjbm1mX2Z1bmN0aW9uX0hhcm1vbnkuUiIpCmBgYAojIyBVTUFQCmBgYHtyIH0KRGltUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxwdC5zaXplID0gMikKYGBgCiMjIGZlYXR1cmVzIFVNQVAKYGBge3IgZmlnLndpZHRoPTEwfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIlRQNjMiLCJBQ1RBMiIsIklMMTJCIiwiQ05OMSIpKQpgYGAKCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIEhNU0MgdnMgQUNDCmBgYHtyIGZpZy53aWR0aD04LCBlY2hvPVRSVUUscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CnBhdGllbnQuaWRlbnQgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyRwYXRpZW50LmlkZW50ICU+JSBhcy5kYXRhLmZyYW1lKCkKcGF0aWVudC5pZGVudFssMV0gPSBhcy5jaGFyYWN0ZXIocGF0aWVudC5pZGVudFssMV0pCnBhdGllbnQuaWRlbnRbcGF0aWVudC5pZGVudFssMV0gPT0gIkFDQzEiLF0gPSAiSE1TQyIKcGF0aWVudC5pZGVudFssMV0gPSBhcy5mYWN0b3IocGF0aWVudC5pZGVudFssMV0pCmFsbF9hY2NfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBwYXRpZW50LmlkZW50LGNvbC5uYW1lID0gInBhdGllbnQuaWRlbnQiKQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IFNldElkZW50KGFsbF9hY2NfY2FuY2VyX2NlbGxzLCB2YWx1ZSA9InBhdGllbnQuaWRlbnQiKQphY2NfZGVnIDwtIEZpbmRNYXJrZXJzKGFsbF9hY2NfY2FuY2VyX2NlbGxzLCBpZGVudC4xID0gIkhNU0MiLGxvZ2ZjLnRocmVzaG9sZCA9IDEuNSxmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHMpKQplbnJpY2htZW50X2FuYWx5c2lzKGFjY19kZWcsYmFja2dyb3VuZCA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHMpLGZkcl9DdXRvZmYgPSAwLjAxLGlkZW50LjEgPSAiSE1TQyIsaWRlbnQuMiA9ICJBQ0MiLHNob3dfYnkgPSAxKQpgYGAKCiMjIGNlbGwgY3ljbGUgZmlsdGVyaW5nIHsudGFic2V0fQoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmhhbGxtYXJrX25hbWUgPSAiR09fTUlUT1RJQ19DRUxMX0NZQ0xFIgpnZW5lc2V0cyAgPWdldEdtdCgiLi9EYXRhL2guYWxsLnY3LjAuc3ltYm9scy5wbHVzY2MuZ210IikKdmFyX2ZlYXR1cmVzPWFsbF9hY2NfY2FuY2VyX2NlbGxzQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzCmdlbmVJZHM9IGdlbmVzZXRzW1toYWxsbWFya19uYW1lXV1AZ2VuZUlkcwpzY29yZSA8LSBhcHBseShhbGxfYWNjX2NhbmNlcl9jZWxsc0Bhc3NheXMkUk5BQHNjYWxlLmRhdGFbaW50ZXJzZWN0KGdlbmVJZHMsdmFyX2ZlYXR1cmVzKSxdLDIsbWVhbikKYWxsX2FjY19jYW5jZXJfY2VsbHM9QWRkTWV0YURhdGEoYWxsX2FjY19jYW5jZXJfY2VsbHMsc2NvcmUsaGFsbG1hcmtfbmFtZSkKCiNmaWx0ZXI6CmFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQ9YWxsX2FjY19jYW5jZXJfY2VsbHNbLGFsbF9hY2NfY2FuY2VyX2NlbGxzQG1ldGEuZGF0YVtbaGFsbG1hcmtfbmFtZV1dPCAwLjNdCgoKbWluX3RocmVzaG9sZCA9IG1pbihhbGxfYWNjX2NhbmNlcl9jZWxscyRHT19NSVRPVElDX0NFTExfQ1lDTEUpCm1heF90aHJlc2hvbGQgPSBtYXgoYWxsX2FjY19jYW5jZXJfY2VsbHMkR09fTUlUT1RJQ19DRUxMX0NZQ0xFKQpgYGAKCgojIyMgQmVmb3JlIGNjIGZpbHRlcmluZwoKCmBgYHtyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGhhbGxtYXJrX25hbWUpICsgZ2d0aXRsZSgiQmVmb3JlIGNjIGZpbHRlcmluZyIpICAmIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcGxhc21hKG4gPSAxMCwgZGlyZWN0aW9uID0gLTEpLCBsaW1pdHMgPSBjKG1pbl90aHJlc2hvbGQsIG1heF90aHJlc2hvbGQpKQpgYGAKCiMjIyBBZnRlciBjYyBmaWx0ZXJpbmcKCmBgYHtyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLGZlYXR1cmVzID0gaGFsbG1hcmtfbmFtZSkgKyBnZ3RpdGxlKCJBZnRlciBjYyBmaWx0ZXJpbmciKSAmIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcGxhc21hKG4gPSAxMCwgZGlyZWN0aW9uID0gLTEpLCBsaW1pdHMgPSBjKG1pbl90aHJlc2hvbGQsIG1heF90aHJlc2hvbGQpKQpgYGAKIyMgey19CgojIyBFbnJpY2htZW50IGFuYWx5c2lzIGZpbHRlcmVkIEhNU0MgdnMgQUNDCmBgYHtyIGZpZy53aWR0aD04LCBlY2hvPVRSVUUscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CnBhdGllbnQuaWRlbnQgPSBhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkJHBhdGllbnQuaWRlbnQgJT4lIGFzLmRhdGEuZnJhbWUoKQpwYXRpZW50LmlkZW50WywxXSA9IGFzLmNoYXJhY3RlcihwYXRpZW50LmlkZW50WywxXSkKcGF0aWVudC5pZGVudFtwYXRpZW50LmlkZW50WywxXSA9PSAiQUNDMSIsXSA9ICJITVNDIgpwYXRpZW50LmlkZW50WywxXSA9IGFzLmZhY3RvcihwYXRpZW50LmlkZW50WywxXSkKYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsbWV0YWRhdGEgPSBwYXRpZW50LmlkZW50LGNvbC5uYW1lID0gInBhdGllbnQuaWRlbnQiKQphbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkID0gU2V0SWRlbnQoYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCwgdmFsdWUgPSJwYXRpZW50LmlkZW50IikKYWNjX2RlZyA8LSBGaW5kTWFya2VycyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLCBpZGVudC4xID0gIkhNU0MiLGxvZ2ZjLnRocmVzaG9sZCA9IDEuNSxmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCkpCmVucmljaG1lbnRfYW5hbHlzaXMoYWNjX2RlZyxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkKSxmZHJfQ3V0b2ZmID0gMC4wMSxpZGVudC4xID0gIkhNU0MiLGlkZW50LjIgPSAiQUNDIixzaG93X2J5ID0gMSkKYGBgCgojIyBNWUIgZXhwcmVzc2lvbgoKYGBge3IgZmlnLndpZHRoPTEwfQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLHZhbHVlID0gInBhdGllbnQuaWRlbnQiKSAjYWN0aXZlIHNubiBncmFwaApGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9ICJNWUIiLGxhYmVsID0gVCkKYGBgCgojIyBDTlYgey50YWJzZXR9CgoKCmBgYHtyfQojc2V0IGNlbGwgdHlwZXMKbmV3LmNsdXN0ZXIuaWRzIDwtIGMoImNhbmNlciIsICMwCiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMQogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzIKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMzCiAgICAgICAgICAgICAgICAgICAgICJFbmRvdGhlbGlhbCIsICM0CiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjNQogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzYKICAgICAgICAgICAgICAgICAgICAgIkNBRiIsICM3CiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjOAogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzkKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxMAogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzExCiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMTIKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxMwogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzE0CiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMTUKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxNgogICAgICAgICAgICAgICAgICAgICAiV0JDIiwgIzE3CiAgICAgICAgICAgICAgICAgICAgICJDQUYiICMxOAogICAgICAgICAgICAgICAgICAgICApCmBgYAoKCmBgYHtyIGZpZy5zaG93PSdoaWRlJ30KI3JlbmFtZSBpZGVudHM6CmFjY19hbGxfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfYWxsX2NlbGxzLHZhbHVlID0gIlJOQV9zbm5fcmVzLjEiKSAjYWN0aXZlIHNubiBncmFwaApuYW1lcyhuZXcuY2x1c3Rlci5pZHMpIDwtIGxldmVscyhhY2NfYWxsX2NlbGxzKSAjYWRkIHNubiBncmFwaCBsZXZlbHMgdG8gbmV3LmNsdXN0ZXIuaWRzCmFjY19hbGxfY2VsbHNAbWV0YS5kYXRhW1sic2V1cmF0X2NsdXN0ZXJzIl1dID0gYWNjX2FsbF9jZWxsc0BtZXRhLmRhdGFbWyJSTkFfc25uX3Jlcy4xIl1dCmFjY19hbGxfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfYWxsX2NlbGxzLHZhbHVlID0gInNldXJhdF9jbHVzdGVycyIpCmFjY19hbGxfY2VsbHMgPC0gUmVuYW1lSWRlbnRzKGFjY19hbGxfY2VsbHMsIG5ldy5jbHVzdGVyLmlkcykgCgojIGRpdmlkZSAiY2FuY2VyIiBpbnRvIHBhdGllbnRzOgpjZWxsX3R5cGVzID0gYWNjX2FsbF9jZWxsc0BhY3RpdmUuaWRlbnQgJT4lIGFzLmRhdGEuZnJhbWUoKQpjZWxsX3R5cGVzWywxXTwtIGFzLmNoYXJhY3RlcihjZWxsX3R5cGVzWywxXSkKY2VsbF90eXBlcyA9IGNiaW5kKGNlbGxfdHlwZXMsYWNjX2FsbF9jZWxscyRwYXRpZW50LmlkZW50KSAlPiUgc2V0bmFtZXMob2xkID0gbmFtZXMoLiksIAogICAgICAgICBuZXcgPSBjKCdjZWxsX3R5cGUnLCdwYXRpZW50JykpCmNlbGxfdHlwZXNbY2VsbF90eXBlcyRjZWxsX3R5cGUgPT0gImNhbmNlciIsXSA9IGNlbGxfdHlwZXNbY2VsbF90eXBlcyRjZWxsX3R5cGUgPT0gImNhbmNlciIsMl0KCgojIGhtc2Nfcm93cyA9IChzdGFydHNXaXRoKHggPSByb3duYW1lcyhjZWxsX3R5cGVzKSxwcmVmaXggPSAiQUNDLnBsYXRlMiIpIHwgc3RhcnRzV2l0aCh4ID0gcm93bmFtZXMoY2VsbF90eXBlcykscHJlZml4ID0gIkFDQzEuIikpICYgY2VsbF90eXBlc1ssMV0gPT0gImNhbmNlciIgCiMgYWNjX3Jvd3MgPSAhKHN0YXJ0c1dpdGgoeCA9IHJvd25hbWVzKGNlbGxfdHlwZXMpLHByZWZpeCA9ICJBQ0MucGxhdGUyIikgfCBzdGFydHNXaXRoKHggPSByb3duYW1lcyhjZWxsX3R5cGVzKSxwcmVmaXggPSAiQUNDMS4iKSkgJiBjZWxsX3R5cGVzWywxXSA9PSAiY2FuY2VyIiAKIyBjZWxsX3R5cGVzWywxXVtobXNjX3Jvd3NdICA9ICJITVNDIgojIGNlbGxfdHlwZXNbLDFdW2FjY19yb3dzXSAgPSAiQUNDIgoKI2FkZCB0byBtZXRhZGF0YToKY2VsbF90eXBlc1ssMl0gPSBOVUxMIApjZWxsX3R5cGVzW2NlbGxfdHlwZXMkY2VsbF90eXBlID09ICJBQ0MxIixdID0gIkhNU0MiCmFjY19hbGxfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPWFjY19hbGxfY2VsbHMgLG1ldGFkYXRhID0gY2VsbF90eXBlcyxjb2wubmFtZSA9ICJjZWxsLnR5cGUiKQpgYGAKIyMjIENOViBVTUFQIAoKYGBge3IgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KGluZmVyY252KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYWNjX2Fubm90YXRpb24gID0gYXMuZGF0YS5mcmFtZShhY2NfYWxsX2NlbGxzQG1ldGEuZGF0YVssImNlbGwudHlwZSIsZHJvcCA9IEZdKQphY2NfYW5ub3RhdGlvbiA9IGFjY19hbm5vdGF0aW9uICU+JSByb3duYW1lc190b19jb2x1bW4oIm9yaWcuaWRlbnQiKSAKYWNjX2Fubm90YXRpb24gPSBhY2NfYW5ub3RhdGlvbiAlPiUgbXV0YXRlKG9yaWcuaWRlbnQgPSBnc3ViKHggPSBhY2NfYW5ub3RhdGlvbiRvcmlnLmlkZW50LHBhdHRlcm4gPSAiXFwuIiwgcmVwbGFjZW1lbnQgPSAiLSIpICU+JSAKICBnc3ViKHBhdHRlcm4gPSAiXyIsIHJlcGxhY2VtZW50ID0gIi0iKSkKICAKCndyaXRlLnRhYmxlKGFjY19hbm5vdGF0aW9uLCAiLi9EYXRhL2luZmVyQ05WL2FjY19hbm5vdGF0aW9uLnR4dCIsIGFwcGVuZCA9IEZBTFNFLCAKICAgICAgICAgICAgc2VwID0gIlx0IiwgZGVjID0gIi4iLHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGKQoKaW5mZXJjbnZfb2JqID0gQ3JlYXRlSW5mZXJjbnZPYmplY3QocmF3X2NvdW50c19tYXRyaXg9Ii4vRGF0YS9pbmZlckNOVi9hbGwuNGljbnYudHh0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9Ii4vRGF0YS9pbmZlckNOVi9hY2NfYW5ub3RhdGlvbi50eHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWxpbT0iXHQiLGdlbmVfb3JkZXJfZmlsZT0iLi9EYXRhL2luZmVyQ05WL2dlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHJlZl9ncm91cF9uYW1lcz1jKCJDQUYiLCAiRW5kb3RoZWxpYWwiLCAiV0JDIikpICNncm91cHMgb2Ygbm9ybWFsIGNlbGxzCgppbmZlcmNudl9vYmpfZGVmYXVsdCA9IGluZmVyY252OjpydW4oaW5mZXJjbnZfb2JqLCBjdXRvZmY9MSwgb3V0X2Rpcj0nLi9EYXRhL2luZmVyQ05WL2luZmVyY252X291dHB1dCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1ULCBwbG90X3N0ZXBzPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVub2lzZT1UUlVFLCBITU09RkFMU0UsIG5vX3ByZWxpbV9wbG90PVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbmdfcmVzPTMwMCkKcGxvdF9jbnYoaW5mZXJjbnZfb2JqX2RlZmF1bHQsIG91dHB1dF9mb3JtYXQgPSAicG5nIiwgIHdyaXRlX2V4cHJfbWF0cml4ID0gRkFMU0Usb3V0X2RpciA9ICIuL0RhdGEvaW5mZXJDTlYvIixwbmdfcmVzCT04MDAsb2JzX3RpdGxlID0gIk1hbGlnbmFudCBjZWxscyIscmVmX3RpdGxlID0gIk5vcm1hbCBjZWxscyIpCgoKY2x1c3Rlci5pbmZvPUZldGNoRGF0YShhY2NfYWxsX2NlbGxzLGMoImlkZW50Iiwib3JpZy5pZGVudCIsIlVNQVBfMSIsIlVNQVBfMiIsIm5Db3VudF9STkEiLCJuRmVhdHVyZV9STkEiLCJwZXJjZW50Lm10IiwicGF0aWVudC5pZGVudCIsInNldXJhdF9jbHVzdGVycyIpKQpjbHVzdGVyLmluZm8kY2VsbD1yb3duYW1lcyhjbHVzdGVyLmluZm8pCgpsaWJyYXJ5KGxpbW1hKQpzbW9vdGhlZD1hcHBseShpbmZlcmNudl9vYmpfZGVmYXVsdEBleHByLmRhdGEsMix0cmljdWJlTW92aW5nQXZlcmFnZSwgc3Bhbj0wLjAxKQpjbnNpZz1zcXJ0KGFwcGx5KChzbW9vdGhlZC0xKV4yLDIsbWVhbikpCnVtYXA9Y2x1c3Rlci5pbmZvCm5hbWVzKGNuc2lnKT11bWFwJGNlbGwKCmFjY19hbGxfY2VsbHMgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjX2FsbF9jZWxscywgbWV0YWRhdGEgPSBjbnNpZywgY29sLm5hbWUgPSAiY29weW51bWJlciIpCmFjY19hbGxfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfYWxsX2NlbGxzLHZhbHVlID0gImNlbGwudHlwZSIpCkZlYXR1cmVQbG90KGFjY19hbGxfY2VsbHMsICJjb3B5bnVtYmVyIixwdC5zaXplID0gMSwgY29scyA9IGMoImxpZ2h0Ymx1ZSIsIm9yYW5nZSIsInJlZCIsImRhcmtyZWQiKSxsYWJlbCA9IFQscmVwZWwgPSBUKQpgYGAKCgojIyMgQ05WIHBsb3QgCgohW0NOViBwbG90XSgvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvSE1TQy9EYXRhL2luZmVyQ05WL2luZmVyY252LnBuZykKIyMgey19CgojIyBDTlYgc3VidHlwZXMKCmBgYHtyfQpjbnZfc3VidHlwZXMgPSBhcy5kYXRhLmZyYW1lKGN1dHJlZShpbmZlcmNudl9vYmpfZGVmYXVsdEB0dW1vcl9zdWJjbHVzdGVyc1tbImhjIl1dW1siSE1TQyJdXSwgayA9IDIpKQpuYW1lcyhjbnZfc3VidHlwZXMpWzFdID0gImNudi5jbHVzdGVyIgpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIi0iLHJlcGxhY2VtZW50ID0gIlxcLiIpCmNudl9zdWJ0eXBlcyBbLDFdID0gYXMuY2hhcmFjdGVyKGNudl9zdWJ0eXBlc1ssMV0pCmluZmVyY252Lm9ic2VydmF0aW9ucyA9IGRhdGEuZnJhbWUoZnJlYWQoZmlsZSA9ICIuL0RhdGEvaW5mZXJDTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zLnR4dCIpLCByb3cubmFtZXM9MSkKbmFtZXNfdG9fa2VlcCA9IGNvbG5hbWVzKGluZmVyY252Lm9ic2VydmF0aW9ucykgJWluJSAoY29sbmFtZXMoYWNjMV9jYW5jZXJfY2VsbHMpICU+JSBnc3ViKHBhdHRlcm4gPSAiXyIscmVwbGFjZW1lbnQgPSAiXFwuIikpCmluZmVyY252Lm9ic2VydmF0aW9ucyA9IGluZmVyY252Lm9ic2VydmF0aW9uc1ssbmFtZXNfdG9fa2VlcF0KYGBgCgpgYGB7cn0Kcm90YXRlIDwtIGZ1bmN0aW9uKHgpIHQoYXBwbHkoeCwgMiwgcmV2KSkKaW5mZXJjbnYub2JzZXJ2YXRpb25zMiA9IGluZmVyY252Lm9ic2VydmF0aW9ucyAlPiUgcm90YXRlKCkgJT4lICByb3RhdGUoKSAlPiUgcm90YXRlKCklPiUgYXMuZGF0YS5mcmFtZSgpIApicmVha3MgPSBjKDAuNzAwODkxODYxNzA0ODU3LAowLjc0MjM2Njk0NTUyODM2OSwKMC43ODM4NDIwMjkzNTE4ODEsCjAuODI1MzE3MTEzMTc1MzkzLAowLjg2Njc5MjE5Njk5ODkwNSwKMC45MDgyNjcyODA4MjI0MTcsCjAuOTQ5NzQyMzY0NjQ1OTI4LAowLjk5MTIxNzQ0ODQ2OTQ0LAoxLjAzMjY5MjUzMjI5Mjk1LAoxLjA3NDE2NzYxNjExNjQ2LAoxLjExNTY0MjY5OTkzOTk4LAoxLjE1NzExNzc4Mzc2MzQ5LAoxLjE5ODU5Mjg2NzU4NywKMS4yNDAwNjc5NTE0MTA1MSwKMS4yODE1NDMwMzUyMzQwMiwKMS4zMjMwMTgxMTkwNTc1MykKcGhlYXRtYXAoaW5mZXJjbnYub2JzZXJ2YXRpb25zMixjbHVzdGVyX2NvbHMgPSBGLGNsdXN0ZXJfcm93cyA9IEYsIHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGLCBicmVha3MgPSBicmVha3MsY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihjKCJkYXJrcmVkIiwgIndoaXRlIiwgImRhcmtibHVlIikpKSgxNSksYW5ub3RhdGlvbl9yb3cgPSBjbnZfc3VidHlwZXMpCgpgYGAKCmBgYHtyfQpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIjJcXC4iLHJlcGxhY2VtZW50ID0gIjJfIikKcm93bmFtZXMoY252X3N1YnR5cGVzKSA9IHJvd25hbWVzKGNudl9zdWJ0eXBlcykgJT4lIGdzdWIocGF0dGVybiA9ICIzXFwuIixyZXBsYWNlbWVudCA9ICIzXyIpCmFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBjbnZfc3VidHlwZXMpCmBgYAoKYGBge3J9CkRpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMsZ3JvdXAuYnkgPSAiY252LmNsdXN0ZXIiLHB0LnNpemUgPSAyKQpgYGAKCgojIyBPcmlnaW5hbCBzY29yZQpgYGB7cn0Kb3JpZ2luYWxfbXlvX2dlbmVzID0gYyggIlRQNjMiLCAiVFA3MyIsICJDQVYxIiwgIkNESDMiLCAiS1JUNSIsICJLUlQxNCIsICJBQ1RBMiIsICJUQUdMTiIsICJNWUxLIiwgIkRLSzMiKQpvcmlnaW5hbF9sdW1fZ2VuZXMgPSBjKCJLSVQiLCAiRUhGIiwgIkVMRjUiLCAiS1JUNyIsICJDTEROMyIsICJDTERONCIsICJDRDI0IiwgIkxHQUxTMyIsICJMQ04yIiwgIlNMUEkiICkKYGBgCgoKCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG9yaWdpbmFsX215b19nZW5lcyxsdW1fZ2VuZXMgPSBvcmlnaW5hbF9sdW1fZ2VuZXMpCmBgYAojIyBPcmlnaW5hbCBzY29yZSBvZiBBQ0MxCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG9yaWdpbmFsX215b19nZW5lcyxsdW1fZ2VuZXMgPSBvcmlnaW5hbF9sdW1fZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDAsbXlvX3RocmVzaG9sZCA9IDApCmBgYAoKCiMjIDAuMzUgTW9zdCBjb3JyZWxhdGVkIHNjb3JlIHsudGFic2V0fQoKIyMjIE15byBnZW5lcwoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGNvbGxhcHNlPVR9Cm15b19wcm90ZWluX21hcmtlcnMgPSBjKCJDTk4xIiwgIlRQNjMiLCJBQ1RBMiIpCnRvcF9teW8gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IG15b19wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4zNSkKcHJpbnQoIk51bWJlciBvZiBnZW5lcyA9ICIgJT4lIHBhc3RlKGxlbmd0aCh0b3BfbXlvKSkpCm1lc3NhZ2UoIk5hbWVzIG9mIGdlbmVzOiIpCnRvcF9teW8gJT4lIGhlYWQoMzApCm1lc3NhZ2UoIkdlbmVzIHRoYXQgYWxzbyBhcGVhcmVkIGluIHRoZSBvcmlnaW5hbCBzY29yZToiKQpiYXNlOjppbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApgYGAKYGBge3J9Cm15b19lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbXlvLGJhY2tncm91bmQgPSByb3duYW1lcyhhY2MxX2NhbmNlcl9jZWxscyksaG9tZXIgPSBULHRpdGxlID0gIm15byB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbXlvX2VucmljaF9yZXMKYGBgCiMjIyBMdW0gZ2VuZXMKYGBge3J9Cmx1bV9wcm90ZWluX21hcmtlcnMgPSBjKCJLSVQiKQp0b3BfbHVtICA9IHRvcF9jb3JyZWxhdGVkKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscywgZ2VuZXMgPSBsdW1fcHJvdGVpbl9tYXJrZXJzLHRocmVzaG9sZCA9IDAuMzUsbl92YXJnZW5lcyA9IDUwMDApCnByaW50KCJOdW1iZXIgb2YgZ2VuZXMgPSAiICU+JSBwYXN0ZShsZW5ndGgodG9wX2x1bSkpKQptZXNzYWdlKCJOYW1lcyBvZiBnZW5lczoiKQp0b3BfbHVtICU+JSBoZWFkKDMwKQptZXNzYWdlKCJHZW5lcyB0aGF0IGFsc28gYXBlYXJlZCBpbiB0aGUgb3JpZ2luYWwgc2NvcmU6IikKYmFzZTo6aW50ZXJzZWN0KHRvcF9sdW0sb3JpZ2luYWxfbHVtX2dlbmVzKSAKYGBgCgpgYGB7cn0KbHVtX2VucmljaF9yZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcF9sdW0sYmFja2dyb3VuZCA9IHJvd25hbWVzKGFjYzFfY2FuY2VyX2NlbGxzKSxob21lciA9IFQsdGl0bGUgPSAibHVtIHRvcCBlbnJpY2htZW50IixjdXN0b21fcGF0aHdheXMgPSBsdW1pbmFsX2dzKQpsdW1fZW5yaWNoX3JlcwpgYGAKIyMjIHRvcCBjb3JyZWxhdGVkIHNjb3JlCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IHRvcF9teW8sbHVtX2dlbmVzID0gdG9wX2x1bSxsdW1fdGhyZXNob2xkID0gMCxteW9fdGhyZXNob2xkID0gMCkKYGBgCgoKIyMjICBlbnJpY2hlZCBnZW5lcyBzY29yZQpgYGB7cn0Kcm93bmFtZXMobHVtX2VucmljaF9yZXMpID0gbHVtX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hfcmVzWzEsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLGx1bV9wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpyb3duYW1lcyhteW9fZW5yaWNoX3JlcykgPSBteW9fZW5yaWNoX3JlcyRwYXRod2F5X25hbWUKbXlvX2VucmljaGVkX2dlbmVzID0gbXlvX2VucmljaF9yZXNbMSwiZ2VuZUlEIl0gJT4lIHN0cnNwbGl0KHNwbGl0ID0gIi8iKSAlPiUgLltbMV1dICU+JSBjKC4sbXlvX3Byb3RlaW5fbWFya2VycykgI2FkZCBvcmlnaW5hbCBtYXJrZXJzCmBgYAoKYGBge3J9CmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2VucmljaGVkX2dlbmVzLGx1bV9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lcyxsdW1fdGhyZXNob2xkID0gLTIuNSxteW9fdGhyZXNob2xkID0gLTIuNSkKYGBgCgoKIyMgey19CgoKIyMgIDAuMiBNb3N0IGNvcnJlbGF0ZWQgc2NvcmUgey50YWJzZXR9CgojIyMgIG15byBHZW5lcwoKYGBge3J9CgpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm5fdmFyZ2VuZXMgPSAyMDAwCm15b19wcm90ZWluX21hcmtlcnMgPSBjKCJDTk4xIiwgIlRQNjMiLCJBQ1RBMiIpCnRvcF9teW8gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IG15b19wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4yLG5fdmFyZ2VuZXMgPSBuX3ZhcmdlbmVzKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9teW8pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX215byAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbXlvLG9yaWdpbmFsX215b19nZW5lcykgCm15b19lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbXlvLGJhY2tncm91bmQgPSBWYXJpYWJsZUZlYXR1cmVzKGFjYzFfY2FuY2VyX2NlbGxzKSAlPiUgaGVhZChuX3ZhcmdlbmVzKSxob21lciA9IFQsdGl0bGUgPSAibXlvIHRvcCBlbnJpY2htZW50IixjdXN0b21fcGF0aHdheXMgPSBsdW1pbmFsX2dzKQpteW9fZW5yaWNoX3JlcwpgYGAKCiMjIyAgTHVtIEdlbmVzCgpgYGB7cn0KYWNjMV9jYW5jZXJfY2VsbHMgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxuZmVhdHVyZXMgPSAxNTAwMCkKYGBgCgpgYGB7cn0KbHVtX3Byb3RlaW5fbWFya2VycyA9IGMoIktJVCIpCm5fdmFyZ2VuZXMgPSAyMDAwCnRvcF9sdW0gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IGx1bV9wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4zLG5fdmFyZ2VuZXMgPSBuX3ZhcmdlbmVzKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9sdW0pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX2x1bSAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbHVtLG9yaWdpbmFsX2x1bV9nZW5lcykgCgpsdW1fZW5yaWNoX3JlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wX2x1bSxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhY2MxX2NhbmNlcl9jZWxscykgJT4lIGhlYWQobl92YXJnZW5lcyksaG9tZXIgPSBULHRpdGxlID0gImx1bSB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbHVtX2VucmljaF9yZXMKY2FsY3VsYXRlX3Njb3JlKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscyxteW9fZ2VuZXMgPSB0b3BfbXlvLGx1bV9nZW5lcyA9IHRvcF9sdW0sbHVtX3RocmVzaG9sZCA9IDIsbXlvX3RocmVzaG9sZCA9IDEpCmBgYAoKCmBgYHtyfQpteW9faW50ZXJzZWN0ZWQgPSBpbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApsdW1faW50ZXJzZWN0ZWQgPSBpbnRlcnNlY3QodG9wX2x1bSxvcmlnaW5hbF9sdW1fZ2VuZXMpIAptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2ludGVyc2VjdGVkCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2ludGVyc2VjdGVkCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2ludGVyc2VjdGVkLGx1bV9nZW5lcyA9IGx1bV9pbnRlcnNlY3RlZCxsdW1fdGhyZXNob2xkID0gMixteW9fdGhyZXNob2xkID0gMSkKYGBgCgoKCiMjIyBlbnJpY2hlZCBnZW5lcwpgYGB7cn0Kcm93bmFtZXMobHVtX2VucmljaF9yZXMpID0gbHVtX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hfcmVzWzMsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLGx1bV9wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpyb3duYW1lcyhteW9fZW5yaWNoX3JlcykgPSBteW9fZW5yaWNoX3JlcyRwYXRod2F5X25hbWUKbXlvX2VucmljaGVkX2dlbmVzID0gbXlvX2VucmljaF9yZXNbMywiZ2VuZUlEIl0gJT4lIHN0cnNwbGl0KHNwbGl0ID0gIi8iKSAlPiUgLltbMV1dICU+JSBjKC4sbXlvX3Byb3RlaW5fbWFya2VycykgI2FkZCBvcmlnaW5hbCBtYXJrZXJzCmBgYAoKYGBge3J9Cm1lc3NhZ2UoImdlbmVzIGluIG15byBzY29yZToiKQpteW9fZW5yaWNoZWRfZ2VuZXMKCm1lc3NhZ2UoImdlbmVzIGluIGx1bSBzY29yZToiKQpsdW1fZW5yaWNoZWRfZ2VuZXMKCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2VucmljaGVkX2dlbmVzLGx1bV9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lcyxsdW1fdGhyZXNob2xkID0gMCxteW9fdGhyZXNob2xkID0gLTEpCmBgYAoKCiMjIyBlbnJpY2hlZCBnZW5lcyBhbmQgaW4gb3JpZ2luYWwgc2NvcmUKYGBge3J9Cm15b19lbnJpY2hlZF9nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lc1tteW9fZW5yaWNoZWRfZ2VuZXMgJWluJSBvcmlnaW5hbF9teW9fZ2VuZXNdCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lc1tsdW1fZW5yaWNoZWRfZ2VuZXMgJWluJSBvcmlnaW5hbF9sdW1fZ2VuZXNdCgptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2VucmljaGVkX2dlbmVzCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2VucmljaGVkX2dlbmVzCgpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lcyxsdW1fZ2VuZXMgPSBsdW1fZW5yaWNoZWRfZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDIsbXlvX3RocmVzaG9sZCA9IDIpCmBgYAoKIyMgey19CgoKIyBIUFYKCk9ubHkgSE1TQyBjYW5jZXIgY2VsbHM6CgpgYGB7cn0KSFBWMzNfUDMgPSBmcmVhZCgiLi9EYXRhL0hQVjMzX1AzLnR4dCIsY29sLm5hbWVzID0gYygicGxhdGUiLCJyZWFkcyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCkhQVjMzX1AzLmRmID0gSFBWMzNfUDMgJT4lIG11dGF0ZSgKICBwbGF0ZSA9IGdzdWIoeCA9SFBWMzNfUDMkcGxhdGUsIHJlcGxhY2VtZW50ID0gIiIscGF0dGVybiA9ICJfLiokIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICItUCIscmVwbGFjZW1lbnQgPSAiLlAiKSAKICAlPiUgZ3N1YihwYXR0ZXJuID0gIi0iLHJlcGxhY2VtZW50ID0gIl8iLCkKICApCkhQVjMzX1AzLmRmID0gSFBWMzNfUDMuZGYgJT4lIGRwbHlyOjpmaWx0ZXIoSFBWMzNfUDMuZGYkcGxhdGUgJWluJSBjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykpCnJvd25hbWVzKEhQVjMzX1AzLmRmKSAgPC0gSFBWMzNfUDMuZGYkcGxhdGUKSFBWMzNfUDMuZGYkcGxhdGUgPSBOVUxMCgoKSFBWMzNfUDIgPSBmcmVhZCgiLi9EYXRhL0hQVjMzX1AyLnR4dCIsY29sLm5hbWVzID0gYygicGxhdGUiLCJyZWFkcyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCkhQVjMzX1AyLmRmID0gSFBWMzNfUDIgJT4lIG11dGF0ZSgKICBwbGF0ZSA9IGdzdWIoeCA9SFBWMzNfUDIkcGxhdGUsIHJlcGxhY2VtZW50ID0gIiIscGF0dGVybiA9ICJfLiokIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICJwbGF0ZTItIixyZXBsYWNlbWVudCA9ICJwbGF0ZTJfIiwpCiAgJT4lIGdzdWIocGF0dGVybiA9ICItIixyZXBsYWNlbWVudCA9ICJcXC4iLCkKICApCkhQVjMzX1AyLmRmID0gSFBWMzNfUDIuZGYgJT4lIGRwbHlyOjpmaWx0ZXIoSFBWMzNfUDIuZGYkcGxhdGUgJWluJSBjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykpCnJvd25hbWVzKEhQVjMzX1AyLmRmKSAgPC0gSFBWMzNfUDIuZGYkcGxhdGUKSFBWMzNfUDIuZGYkcGxhdGUgPSBOVUxMCgpIUFYzMyA9IHJiaW5kKEhQVjMzX1AzLmRmLEhQVjMzX1AyLmRmKQphY2MxX2NhbmNlcl9jZWxscyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLG1ldGFkYXRhID0gSFBWMzMsY29sLm5hbWUgPSAiSFBWMzMucmVhZHMiKQpGZWF0dXJlUGxvdChhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9ICJIUFYzMy5yZWFkcyIsbWF4LmN1dG9mZiA9IDQwKQpgYGAKYGBge3J9CmRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9ICJIUFYzMy5yZWFkcyIpCnByaW50KAogIGRhdGEgJT4lIAogIGdncGxvdChhZXMoIHg9SFBWMzMucmVhZHMpKSArIAogIGdlb21fZGVuc2l0eSgpCikKYGBgCmBgYHtyfQpocHYzM19wb3NpdGl2ZSA9IEhQVjMzICU+JSBkcGx5cjo6bXV0YXRlKGhwdjMzX3Bvc2l0aXZlID0gY2FzZV93aGVuKHJlYWRzID49IDc1MCB+InN0cm9uZyBwb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZHMgPCA3NTAgJiByZWFkcyA+IDEwIH4gInBvc2l0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkcyA8IDEwIH4gIm5lZ2F0aXZlIikKKQoKCgpocHYzM19wb3NpdGl2ZSRyZWFkcyA9IE5VTEwKYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IGhwdjMzX3Bvc2l0aXZlKQpgYGAKCmBgYHtyfQpEaW1QbG90KG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLGdyb3VwLmJ5ICA9IGMoImhwdjMzX3Bvc2l0aXZlIikscHQuc2l6ZSA9IDIpCmBgYAoKCiMgY05NRgpgYGB7cn0KbGlicmFyeShyZXRpY3VsYXRlKQpgYGAKCmBgYHtyfQojd3JpdGUgZXhwcmVzc2lvbgphY2MxX2NhbmNlcl9jZWxscyA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLG5mZWF0dXJlcyA9IDIwMDApCnZhcmdlbmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscykKaG1zY19leHByZXNzaW9uID0gdChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGFjYzFfY2FuY2VyX2NlbGxzLHNsb3Q9J2RhdGEnKSkpCmhtc2NfZXhwcmVzc2lvbiA9IDIqKmhtc2NfZXhwcmVzc2lvbiAjY29udmVydCBmcm9tIGxvZzIodHBtKzEpIHRvIHRwbQpobXNjX2V4cHJlc3Npb24gPSBobXNjX2V4cHJlc3Npb24tMQojIGhtc2NfZXhwcmVzc2lvbiA9IGhtc2NfZXhwcmVzc2lvblssIWNvbFN1bXMoaG1zY19leHByZXNzaW9uPT0wLCBuYS5ybT1UUlVFKT09bnJvdyhobXNjX2V4cHJlc3Npb24pXSAjZGVsZXRlIHJvd3MgdGhhdCBoYXZlIGFsbCAwCmhtc2NfZXhwcmVzc2lvbiA9IGhtc2NfZXhwcmVzc2lvblssdmFyZ2VuZXNdCndyaXRlLnRhYmxlKHggPSBobXNjX2V4cHJlc3Npb24gLGZpbGUgPSAnLi9EYXRhL2NOTUYvaG1zY19leHByZXNzaW9uRGF0YV8yS3ZhcmdlbmVzLnR4dCcsc2VwID0gIlx0IikKYGBgCgoKCmBgYHtweXRob24gZXZhbD1GfQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKbmFtZSA9ICdITVNDX2NOTUZfMkt2YXJnZW5lcycKb3V0ZGlyID0gJy4vRGF0YS9jTk1GJwpLX3JhbmdlID0gbnAuYXJhbmdlKDMsMTApCmNubWZfb2JqID0gY05NRihvdXRwdXRfZGlyPW91dGRpciwgbmFtZT1uYW1lKQpjb3VudHNfZm49Jy4vRGF0YS9jTk1GL2htc2NfZXhwcmVzc2lvbkRhdGFfMkt2YXJnZW5lcy50eHQnCnRwbV9mbiA9IGNvdW50c19mbiAjIyBUaGlzIGlzIGEgd2VpcmQgY2FzZSB3aGVyZSBiZWNhdXNlIHRoaXMgZGF0YXNldCBpcyBub3QgMycgZW5kIHVtaSBzZXF1ZW5jaW5nLCB3ZSBvcHRlZCB0byB1c2UgdGhlIFRQTSBtYXRyaXggYXMgdGhlIGlucHV0IG1hdHJpeCByYXRoZXIgdGhhbiB0aGUgY291bnQgbWF0cml4Cgpjbm1mX29iai5wcmVwYXJlKGNvdW50c19mbj1jb3VudHNfZm4sIGNvbXBvbmVudHM9S19yYW5nZSwgc2VlZD0xNCx0cG1fZm49dHBtX2ZuKQpgYGAKCmBgYHtweXRob24gZXZhbD1GfQpjbm1mX29iai5mYWN0b3JpemUod29ya2VyX2k9MCwgdG90YWxfd29ya2Vycz0xKQpgYGAKCmBgYHtweXRob24gZXZhbD1GfQpjbm1mX29iai5jb21iaW5lKCkKY25tZl9vYmoua19zZWxlY3Rpb25fcGxvdCgpCmBgYAojIyBTYXZlIG9iamVjdApgYGB7cHl0aG9uIGV2YWw9Rn0KaW1wb3J0IHBpY2tsZQpmID0gb3BlbignLi9EYXRhL2NOTUYvSE1TQ19jTk1GXzJLdmFyZ2VuZXMvY25tZl9vYmoucGNrbCcsICd3YicpCnBpY2tsZS5kdW1wKGNubWZfb2JqLCBmKQpmLmNsb3NlKCkKYGBgCgoKIyMgTG9hZCBvYmplY3QKYGBge3B5dGhvbn0KZnJvbSBjbm1mIGltcG9ydCBjTk1GCmltcG9ydCBwaWNrbGUKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl8yS3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCgpgYGB7cHl0aG9ufQpzZWxlY3RlZF9rID0gNApkZW5zaXR5X3RocmVzaG9sZCA9IDAuMQpjbm1mX29iai5jb25zZW5zdXMoaz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKdXNhZ2Vfbm9ybSwgZ2VwX3Njb3JlcywgZ2VwX3RwbSwgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKYGBgCgpgYGB7cn0KIyBjYWxjdWxhdGUgdXNhZ2UgYnkgZXhwcmVzc2lvbipnZW5lcyBjb2VmczoKZ2VwX3Njb3JlcyA9IHB5JGdlcF9zY29yZXMKYWxsX21ldGFnZW5lcz0gcHkkdXNhZ2Vfbm9ybQpgYGAKCiMjIEVucmljaG1lbnQgYW5hbHlzaXMgYnkgdG9wIDIwMCBnZW5lcyBvZiBlYWNoIHByb2dyYW0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgcmVzdWx0cz0naGlkZSd9CnBsdF9saXN0ID0gbGlzdCgpCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwyMDApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3AsYmFja2dyb3VuZCA9IHJvd25hbWVzKGdlcF9zY29yZXMpLGhvbWVyID0gVCx0aXRsZSA9IAogICAgICAgICAgICAgICAgICAgIGksc2lsZW50ID0gVCxyZXR1cm5fYWxsID0gVCkKICAgCiAgcGx0X2xpc3RbW2ldXSA9IHJlcyRwbHQKfQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsdF9saXN0KQpgYGAKYGBge3J9CnBsdF9saXN0ID0gbGlzdCgpCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwxMCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICBtZXNzYWdlKHBhc3RlKCJwcm9ncmFtICIsaSwidG9wIGdlbmVzOiIpKQogIHByaW50KHRvcCkKCn0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CiMgTWFrZSBtZXRhZ2VuZSBuYW1lcwpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgY29sbmFtZXMoYWxsX21ldGFnZW5lcylbaV0gPSAibWV0YWdlbmUuIiAlPiUgcGFzdGUwKGkpCn0KCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgbWV0YWdlX21ldGFkYXRhID0gYWxsX21ldGFnZW5lcyAlPiUgc2VsZWN0KGkpCiAgYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsZmVhdHVyZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSkKCmBgYAojIyBhc3NpZ25tZW50IFVNQVAKYGBge3J9Cmxhcmdlcl9ieSA9IDIKbWVzc2FnZShwYXN0ZSgibGFyZ2VyX2J5ID0gIiwgbGFyZ2VyX2J5KSkKYWNjMV9jYW5jZXJfY2VsbHMgPSBwcm9ncmFtX2Fzc2lnbm1lbnQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLGxhcmdlcl9ieSA9IGxhcmdlcl9ieSxwcm9ncmFtX25hbWVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpCnNlbGVjdGVkX2sgPTQKY29sb3JzID0gIHJhaW5ib3coc2VsZWN0ZWRfaykKY29sb3JzID0gYyhjb2xvcnMsImdyZXkiKQpEaW1QbG90KGFjYzFfY2FuY2VyX2NlbGxzLGdyb3VwLmJ5ID0gInByb2dyYW0uYXNzaWdubWVudCIscHQuc2l6ZSA9IDIsY29scyA9Y29sb3JzKQpgYGAgCnNob3cgY2VsbCBjeWNsZSBwcm9ncmFtOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpoYWxsbWFya19uYW1lID0gIkdPX01JVE9USUNfQ0VMTF9DWUNMRSIKZ2VuZXNldHMgID1HU0VBQmFzZTo6Z2V0R210KCIuL0RhdGEvaC5hbGwudjcuMC5zeW1ib2xzLnBsdXNjYy5nbXQiKQp2YXJfZmVhdHVyZXM9YWNjMV9jYW5jZXJfY2VsbHNAYXNzYXlzJFJOQUB2YXIuZmVhdHVyZXMKZ2VuZUlkcz0gZ2VuZXNldHNbW2hhbGxtYXJrX25hbWVdXUBnZW5lSWRzCnNjb3JlIDwtIGFwcGx5KGFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5cyRSTkFAZGF0YVtpbnRlcnNlY3QoZ2VuZUlkcyx2YXJfZmVhdHVyZXMpLF0sMixtZWFuKQphY2MxX2NhbmNlcl9jZWxscz1BZGRNZXRhRGF0YShhY2MxX2NhbmNlcl9jZWxscyxzY29yZSxoYWxsbWFya19uYW1lKQpgYGAKYGBge3J9CkZlYXR1cmVQbG90KG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gaGFsbG1hcmtfbmFtZSkKCmBgYApgYGB7cn0KY2NfdnNfcHJvZ3JhbTIgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoIm1ldGFnZW5lLjIiLGhhbGxtYXJrX25hbWUpKQpjb3IoY2NfdnNfcHJvZ3JhbTJbMV0sY2NfdnNfcHJvZ3JhbTJbMl0pCmBgYAoKIyMgQ29tcGFyaXNpb25zCgoKYGBge3J9CmNudl92c19ocHYgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoImNudi5jbHVzdGVyIiwiaHB2MzNfcG9zaXRpdmUiKSkKdGVzdCA8LSBmaXNoZXIudGVzdCh0YWJsZShjbnZfdnNfaHB2KSkKZ2diYXJzdGF0cygKICBjbnZfdnNfaHB2LCBjbnYuY2x1c3RlciwgaHB2MzNfcG9zaXRpdmUsCiAgcmVzdWx0cy5zdWJ0aXRsZSA9IEZBTFNFLAogIHN1YnRpdGxlID0gcGFzdGUwKAogICAgIkZpc2hlcidzIGV4YWN0IHRlc3QiLCAiLCBwLXZhbHVlID0gIiwKICAgIGlmZWxzZSh0ZXN0JHAudmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0ZXN0JHAudmFsdWUsIDMpKQogICkKKQoKYGBgCgpgYGB7cn0KY252X3ZzX2NubWYgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoInByb2dyYW0uYXNzaWdubWVudCIsImNudi5jbHVzdGVyIikpCmNudl92c19jbm1mID0gY252X3ZzX2NubWYgJT4lIGRwbHlyOjpmaWx0ZXIocHJvZ3JhbS5hc3NpZ25tZW50ID09ICIxIiB8cHJvZ3JhbS5hc3NpZ25tZW50ID09ICIyIiApCnRlc3QgPC0gZmlzaGVyLnRlc3QodGFibGUoY252X3ZzX2NubWYpKQpnZ2JhcnN0YXRzKAogIGNudl92c19jbm1mLCBwcm9ncmFtLmFzc2lnbm1lbnQsIGNudi5jbHVzdGVyLAogIHJlc3VsdHMuc3VidGl0bGUgPSBGQUxTRSwKICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICJGaXNoZXIncyBleGFjdCB0ZXN0IiwgIiwgcC12YWx1ZSA9ICIsCiAgICBpZmVsc2UodGVzdCRwLnZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQodGVzdCRwLnZhbHVlLCAzKSkKICApCikKCmBgYAoKYGBge3J9CmNubWZfdnNfaHB2ID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJwcm9ncmFtLmFzc2lnbm1lbnQiLCJocHYzM19wb3NpdGl2ZSIpKQpjbm1mX3ZzX2hwdiA9IGNubWZfdnNfaHB2ICU+JSBkcGx5cjo6ZmlsdGVyKHByb2dyYW0uYXNzaWdubWVudCA9PSAiMSIgfHByb2dyYW0uYXNzaWdubWVudCA9PSAiMiIgKQp0ZXN0IDwtIGZpc2hlci50ZXN0KHRhYmxlKGNubWZfdnNfaHB2KSkKZ2diYXJzdGF0cygKICBjbm1mX3ZzX2hwdiwgcHJvZ3JhbS5hc3NpZ25tZW50LCBocHYzM19wb3NpdGl2ZSwKICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAiRmlzaGVyJ3MgZXhhY3QgdGVzdCIsICIsIHAtdmFsdWUgPSAiLAogICAgaWZlbHNlKHRlc3QkcC52YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRlc3QkcC52YWx1ZSwgMykpCiAgKQopCgpgYGAKCmBgYHtyfQpteWJfdnNfY252ID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJjbnYuY2x1c3RlciIsIk1ZQiIpKQpteWJfdnNfY252ICRjbnYuY2x1c3RlciA9IGFzLmNoYXJhY3RlcihteWJfdnNfY252ICRjbnYuY2x1c3RlciApCgpnZ2JveHBsb3QobXliX3ZzX2NudiwgeCA9ICJjbnYuY2x1c3RlciIsIHkgPSAiTVlCIiwKICAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwKICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSsgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsY29tcGFyaXNvbnMgPSBsaXN0KGMoIjEiLCIyIikpKQpgYGAKCgoKYGBge3J9Cm15Yl92c19ocHYgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoImhwdjMzX3Bvc2l0aXZlIiwiTVlCIikpCm15Yl92c19ocHYgJGhwdjMzX3Bvc2l0aXZlID0gYXMuY2hhcmFjdGVyKG15Yl92c19ocHYgJGhwdjMzX3Bvc2l0aXZlICkKCmdnYm94cGxvdChteWJfdnNfaHB2LCB4ID0gImhwdjMzX3Bvc2l0aXZlIiwgeSA9ICJNWUIiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKyBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gIndpbGNveC50ZXN0Iixjb21wYXJpc29ucyA9IGxpc3QoYygicG9zaXRpdmUiLCJuZWdhdGl2ZSIpLGMoInN0cm9uZyBwb3NpdGl2ZSIsICJwb3NpdGl2ZSIpLGMoInN0cm9uZyBwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSkrIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIGRhdGEuZnJhbWUoeT0xNSwgbGFiZWwgPSBwYXN0ZSgiTWVhbj0iLHJvdW5kKG1lYW4oeCksZGlnaXRzID0gMikpKSwgZ2VvbT0idGV4dCIpICt5bGFiKCJsb2cyKE1ZQikiKQpgYGAKCmBgYHtyfQpocHZSZWFkc192c19teWIgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoIkhQVjMzLnJlYWRzIiwiTVlCIikpCmNvcnIgPSBjb3IoaHB2UmVhZHNfdnNfbXliJEhQVjMzLnJlYWRzLGhwdlJlYWRzX3ZzX215YiRNWUIpCnByaW50KCJjb3JyZWxhdGlvbiBvZiBNWUIgYWJkIGhwdjMzX3JlYWRzOiIgJT4lIHBhc3RlKGNvcnIgJT4lIHJvdW5kKGRpZ2l0cyA9IDIpKSApCmBgYApgYGB7cn0KYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5c1tbIlJOQSJdXUBkYXRhICU+JSBhcy5kYXRhLmZyYW1lKCkKYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGNvcihhY2MxX2NhbmNlcl9jZWxsc19kYXRhKQphbm5vdGF0aW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJvcmlnLmlkZW50IikpCgpjb2xvcnMgPC0gYyhzZXEoLTEsMSxieT0wLjAxKSkKICBteV9wYWxldHRlIDwtIGMoImJsdWUiLGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG4gPSBsZW5ndGgoY29sb3JzKS0zKSwgInJlZCIpCiAgCnBodDEgPSBwaGVhdG1hcChhY2MxX2Nvcixhbm5vdGF0aW9uX2NvbCAgPSBhbm5vdGF0aW9uLGZvbnRzaXplID0gNixicmVha3MgPSBjb2xvcnMsIGNvbG9yID0gbXlfcGFsZXR0ZSxzaG93X2NvbG5hbWVzID0gRixzaG93X3Jvd25hbWVzID0gRikKYGBgCgpgYGB7cn0KYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5c1tbIlJOQSJdXUBkYXRhICU+JSBhcy5kYXRhLmZyYW1lKCkKYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGNvcihhY2MxX2NhbmNlcl9jZWxsc19kYXRhKQphbm5vdGF0aW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJjbnYuY2x1c3RlciIpKQoKY29sb3JzIDwtIGMoc2VxKC0xLDEsYnk9MC4wMSkpCiAgbXlfcGFsZXR0ZSA8LSBjKCJibHVlIixjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChuID0gbGVuZ3RoKGNvbG9ycyktMyksICJyZWQiKQogIApwaHQxID0gcGhlYXRtYXAoYWNjMV9jb3IsYW5ub3RhdGlvbl9jb2wgID0gYW5ub3RhdGlvbixmb250c2l6ZSA9IDYsYnJlYWtzID0gY29sb3JzLCBjb2xvciA9IG15X3BhbGV0dGUsc2hvd19jb2xuYW1lcyA9IEYsc2hvd19yb3duYW1lcyA9IEYpCmBgYAoKCmBgYHtyfQpjbnZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoImNudi5jbHVzdGVyIiwib3JpZy5pZGVudCIpKQp0ZXN0IDwtIGZpc2hlci50ZXN0KHRhYmxlKGNudl92c19wbGF0ZSkpCmdnYmFyc3RhdHMoCiAgY252X3ZzX3BsYXRlLCBjbnYuY2x1c3Rlciwgb3JpZy5pZGVudCwKICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAiRmlzaGVyJ3MgZXhhY3QgdGVzdCIsICIsIHAtdmFsdWUgPSAiLAogICAgaWZlbHNlKHRlc3QkcC52YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRlc3QkcC52YWx1ZSwgMykpCiAgKQopCgpgYGAKYGBge3J9CnBsYXRlMyA9IGNudl92c19wbGF0ZSAlPiUgZHBseXI6OmZpbHRlciggb3JpZy5pZGVudCA9PSAiQUNDMS5QMyIpICU+JSByb3duYW1lcygpICU+JSBnc3ViKHBhdHRlcm4gPSAiXyIscmVwbGFjZW1lbnQgPSAiLiIpCnBsYXRlMiA9IGNudl92c19wbGF0ZSAlPiUgZHBseXI6OmZpbHRlciggKG9yaWcuaWRlbnQgPT0gIkFDQy5wbGF0ZTIiKSkgJT4lIHJvd25hbWVzKCkgJT4lIGdzdWIocGF0dGVybiA9ICJfIixyZXBsYWNlbWVudCA9ICIuIikKYGBgCgoKYGBge3J9CmNudl9zdWJ0eXBlcyA9IGNudl9zdWJ0eXBlcyAlPiUgbXV0YXRlKHBsYXRlID0gY2FzZV93aGVuKAogICAgICAgICByb3duYW1lcyhjbnZfc3VidHlwZXMpW3Jvd19udW1iZXIoKV0gJWluJSBwbGF0ZTMgfiAicGxhdGVfMyIsCiAgICAgICAgIHJvd25hbWVzKGNudl9zdWJ0eXBlcylbcm93X251bWJlcigpXSAlaW4lIHBsYXRlMiB+ICJwbGF0ZV8yIiwKICAgICAgICAgVFJVRSAgfiAib2siKSkKCiMgY3JlYXQgY29sb3VycyBmb3IgZWFjaCBncm91cAphbm5vQ29sIDwtIGxpc3QocGxhdGUgPSBjKHBsYXRlXzMgPSAiZ3JlZW4iLHBsYXRlXzIgPSAicmVkIiksY252LmNsdXN0ZXIgPWMoIjEiPSJncmVlbiIsIjIiID0gInJlZCIpKQoKCnBoZWF0bWFwKGluZmVyY252Lm9ic2VydmF0aW9uczIsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyX3Jvd3MgPSBGLCBzaG93X3Jvd25hbWVzID0gRixzaG93X2NvbG5hbWVzID0gRiwgYnJlYWtzID0gYnJlYWtzLGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShyZXYoYygiZGFya3JlZCIsICJ3aGl0ZSIsICJkYXJrYmx1ZSIpKSkoMTUpLGFubm90YXRpb25fcm93ID0gY252X3N1YnR5cGVzLGFubm90YXRpb25fY29sb3JzID0gYW5ub0NvbCkKCmBgYAoKYGBge3J9CmNubWZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoInByb2dyYW0uYXNzaWdubWVudCIsIm9yaWcuaWRlbnQiKSkKY25tZl92c19wbGF0ZT0gY25tZl92c19wbGF0ZSAlPiUgZHBseXI6OmZpbHRlcihwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjEiIHwgcHJvZ3JhbS5hc3NpZ25tZW50ID09ICIyIikKdGVzdCA8LSBmaXNoZXIudGVzdCh0YWJsZShjbm1mX3ZzX3BsYXRlKSkKZ2diYXJzdGF0cygKICBjbm1mX3ZzX3BsYXRlLCBwcm9ncmFtLmFzc2lnbm1lbnQsIG9yaWcuaWRlbnQsCiAgcmVzdWx0cy5zdWJ0aXRsZSA9IEZBTFNFLAogIHN1YnRpdGxlID0gcGFzdGUwKAogICAgIkZpc2hlcidzIGV4YWN0IHRlc3QiLCAiLCBwLXZhbHVlID0gIiwKICAgIGlmZWxzZSh0ZXN0JHAudmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0ZXN0JHAudmFsdWUsIDMpKQogICkKKQoKYGBgCmBgYHtyfQpwbGF0ZV8zID0gY25tZl92c19wbGF0ZSAlPiUgZHBseXI6OmZpbHRlcigocHJvZ3JhbS5hc3NpZ25tZW50ID09IDEgJiBvcmlnLmlkZW50ID09ICJBQ0MxLlAzIikgKSAlPiUgcm93bmFtZXMoKSAKcGxhdGVfMiA9IGNubWZfdnNfcGxhdGUgJT4lIGRwbHlyOjpmaWx0ZXIoKHByb2dyYW0uYXNzaWdubWVudCA9PSAyICYgb3JpZy5pZGVudCA9PSAiQUNDLnBsYXRlMiIpKSAlPiUgcm93bmFtZXMoKQpjZWxscyA9IGxpc3QocGxhdGVfMyA9IHBsYXRlXzMscGxhdGVfMiAgPSBwbGF0ZV8yKQpgYGAKIyMgUmVzdWx0cyB7LnRhYnNldH0KCgojIyMgZXhjZXB0aW9ucwoKYGBge3J9CkRpbVBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsIGNlbGxzLmhpZ2hsaWdodCA9IGNlbGxzLCBjb2xzLmhpZ2hsaWdodCA9IGMoImdyZWVuIiwicmVkIiksIGNvbHMgPSAiZ3JheSIsIG9yZGVyID0gVFJVRSxwdC5zaXplID0gMixzaXplcy5oaWdobGlnaHQgPSAyKSArIGdndGl0bGUoImV4Y2VwdGlvbnMiKQoKYGBgCiMjIyBwcm9ncmFtLmFzc2lnbm1lbnQKCmBgYHtyfQpEaW1QbG90KGFjYzFfY2FuY2VyX2NlbGxzLGdyb3VwLmJ5ID0gInByb2dyYW0uYXNzaWdubWVudCIscHQuc2l6ZSA9IDIsY29scyA9Y29sb3JzKQpgYGAKIyMjIG1ldGFnZW5lLjEKCmBgYHtyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIm1ldGFnZW5lLjEiKSkKCmBgYAojIyMgbWV0YWdlbmUuMgoKYGBge3IgZmlnLndpZHRoPTh9CgpGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIm1ldGFnZW5lLjIiKSkKCmBgYAoK